// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#include "csv.h"


CsvReader::CsvReader(const char* file, char delimeter_):
    mappedFile(0),
	data(0),
	pos(0),
	size(0),
	delimeter(delimeter_),
	buffer(0),
	bufferPos(0),
	bufferCapacity(0),
	endOfLine(true),
	row(-1),
	column(-1),
	value(0)
{
	mappedFile = MappedFile::open(file);
	if (mappedFile != 0) {
		data = mappedFile->data();
		size = mappedFile->size();
	}
}


CsvReader::~CsvReader() {
	if (mappedFile != 0) {
		delete mappedFile;
	}

	if (buffer != 0) {
		delete [] buffer;
	}
}


bool CsvReader::putChar(char ch) {
	if (bufferPos == bufferCapacity) {
		uint newCapacity = bufferCapacity + 1024;
		char* newBuffer = new (nothrow) char[newCapacity];
		if (newBuffer == 0) {
			return false;
		}
		if (bufferPos > 0) {
			memcpy(newBuffer, buffer, bufferPos);
		}
		if (buffer != 0) {
			delete [] buffer;
		}
		buffer = newBuffer;
		bufferCapacity = newCapacity;
	}

	buffer[bufferPos++] = ch;

	return true;
}


bool CsvReader::next() {

	bufferPos = 0;

	if (pos == size) {
		if (buffer != 0) {
			delete [] buffer;
			buffer = 0;
			bufferCapacity = 0;
		}
		return false;
	}
	
	if (endOfLine) {
		row++;
		column = -1;
	    endOfLine = false;
	}

    bool inQuotes = false;
    bool isNull = true;

    while (pos < size) {

        char ch = (char) data[pos++];

        if (ch == '"') {
            inQuotes = !inQuotes;
            isNull = false;
            continue;
        }

        if (ch == '\\' && pos < size) {
            char chNext = data[pos++];
            switch (chNext) {
            case '\\':
            case '"':
                if (!putChar(chNext)) {
                	return false;
                }
                continue;
            case 'n':
                if (!putChar('\n')) {
                	return false;
                }
                continue;
            }
            pos--;
        }

        if (!inQuotes && (ch == delimeter || ch == '\n')) {
	        endOfLine = (ch == '\n');
        	break;
        } else {
            isNull = false;
            if (!putChar(ch)) {
            	return false;
            }
        }
    }

	column++;

	if (isNull) {
		value = 0;
	} else {
		if (!putChar('\0')) {
			return false;
		}
	    value = buffer;
   	}

   	if (pos == size) {
		delete mappedFile;
		mappedFile = 0;
   	}

    return true;
}
